﻿using DotNetCommon.Expressions;
using DotNetCommon.Expressions.Base;
using DotNetCommon.Expressions.Visit;
using DotNetCommon.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace DotNetCommon
{
    /// <summary>
    /// 表达式帮助类
    /// </summary>
    public static class ExpressionHelper
    {
        /// <summary>
        /// 返回lambda表达式树访问到第一个参数的属性名称数组，示例：
        /// <list type="bullet">
        /// <item>
        /// <code>
        /// var cols = ExpressionHelper.GetAccessNames(p => new{ p.Id,p.Name });// 输出: ["Id","Name"]
        /// </code>
        /// </item>
        /// <item>
        /// <code>
        /// var cols = ExpressionHelper.GetAccessNames(p => p.Id);// 输出: ["Id"]
        /// </code>
        /// </item>
        /// <item>
        /// <code>
        /// var cols = ExpressionHelper.GetAccessNames(p => p);// 输出: []
        /// </code>
        /// </item>
        /// <item>
        /// <code>
        /// var cols = ExpressionHelper.GetAccessNames(p => $"id={p.Id},name={p.Name}");// 输出: ["Id","Name"]
        /// </code>
        /// </item>
        /// </list>
        /// </summary>
        /// <param name="lambda"></param>
        /// <returns></returns>
        public static List<string> GetAccessNames(LambdaExpression lambda) => GetAccessMutilNames(lambda).FirstOrDefault();

        /// <summary>
        /// 返回lambda表达式树访问到每个参数的属性名称数组，示例：
        /// <list type="bullet">
        /// <item>
        /// <code>
        /// var cols = ExpressionHelper.GetAccessMutilNames((p1, p2) => new{ p1.Id,p2.Name });// 输出: [["Id"],["Name"]]
        /// </code>
        /// </item>
        /// </list>
        /// </summary>
        /// <param name="lambda"></param>
        /// <remarks>单个参数的输出示例，参考: <seealso cref="ExpressionHelper.GetAccessNames(LambdaExpression)"/></remarks>
        /// <returns></returns>
        public static List<List<string>> GetAccessMutilNames(LambdaExpression lambda)
        {
            if (lambda == null || lambda.Parameters.Count == 0) return new List<List<string>>(0);
            var paras = lambda.Parameters.ToList();
            var list = paras.Select(i => new List<string>()).ToList();

            var root = new ExpressionNode
            {
                Expression = lambda,
                _parameters = lambda.Parameters.ToList(),
                Children = new List<ExpressionNode>()
            };
            VisitTree(root);
            return list;

            //遍历
            void VisitTree(ExpressionNode node)
            {
                if (node.Expression == null) return;
                var visit = _caches[node.NodeType.Value];
                visit.GetAccessName(node, VisitTree, paras, list);
            }
        }

        /// <summary>
        /// 获取初始化的属性名称，示例：
        /// <list type="bullet">
        /// <item>
        /// <code>
        /// var cols = ExpressionHelper.GetInitOrReturnPropNames(() => new{ Id=1,Name="小明" });// 输出: ["Id","Name"]
        /// </code>
        /// </item>
        /// <item>
        /// <code>
        /// var cols = ExpressionHelper.GetInitOrReturnPropNames(p => p.Id);// 输出: ["Id"]
        /// </code>
        /// <code>
        /// var cols = ExpressionHelper.GetInitOrReturnPropNames(p => p);// 输出: []
        /// </code>
        /// <code>
        /// var cols = ExpressionHelper.GetInitOrReturnPropNames(p => new { p.Id, p.Name });// 输出: ["Id","Name"]
        /// </code>
        /// <code>
        /// var cols = ExpressionHelper.GetInitOrReturnPropNames(p => new { Id2 = p.Id, Name2 = p.Name });// 输出: ["Id2","Name2"]
        /// </code>
        /// </item>
        /// </list>
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static List<string> GetInitOrReturnPropNames(LambdaExpression expression)
        {
            var names = new List<string>();
            if (expression.Body.NodeType == ExpressionType.MemberInit)
            {
                var memberInit = expression.Body as MemberInitExpression;
                var bindings = memberInit.Bindings;
                foreach (var item in bindings) names.Add(item.Member.Name);
            }
            else if (expression.Body.NodeType == ExpressionType.New)
            {
                var newExp = expression.Body as NewExpression;
                if (newExp.Type.IsAnonymous())
                {
                    foreach (var item in newExp.Members) names.Add(item.Name);
                }
            }
            //else if (expression.Body.NodeType == ExpressionType.Parameter)
            //{
            //    // p=>p
            //}
            else if (expression.Body.NodeType == ExpressionType.Convert)
            {
                // p=>(object)p.Id
                var unary = expression.Body as UnaryExpression;
                if (unary.Operand is MemberExpression member)
                {
                    if (member.Expression.NodeType != ExpressionType.Parameter) throw new Exception("不能多层获取属性名称(如: p=>p.Id 而不是 p=>p.Info.Id)!");
                    //p=>p.Id
                    names.Add((unary.Operand as MemberExpression).Member.Name);
                }
            }
            else if (expression.Body.NodeType == ExpressionType.MemberAccess)
            {
                //p=>p.Id
                names.Add((expression.Body as MemberExpression).Member.Name);
            }
            return names;
        }

        /// <summary>
        /// 简化lambda表达式，示例：
        /// <code>
        /// //原表达式:
        /// var teachers = new List&lt;Teacher>;
        /// {
        ///     new Teacher{ Id=1,Name="孙悟空"},
        ///     new Teacher{ Id=2,Name="猪八戒"},
        ///     new Teacher{ Id=3,Name="沙僧"}
        ///  };
        ///  Expression&lt;Func&lt;Student, bool>> exp = p => teachers.FirstOrDefault(i => i.Name == "孙悟空").Id == p.TeacherId;
        ///  //进行简化
        ///  var newExp = ExpressionHelper.Reduce(exp);
        ///  //简化结果: (exp: p => Param_0 == p.TeacherId, midValues:{ Param_0: 1})
        /// </code>
        /// </summary>
        /// <param name="lambdaExpression"></param>
        /// <param name="isKeepCallBack">是否保留此表达式,比如: 在 根据lambda解析生成update语句的时候,期望保留 new Person{}</param>
        /// <param name="initParameters">初始的参数列表, 用于特殊情况, 如: 有些手动组装的lambda</param>
        /// <remarks>注意: 由于简化求值而得到的一些中间值放在 midValues 中, 解析生成的 exp 时遇到 parameter 类型时,需要先判断是否在 midValues 中,如果在就当常量对待,否则就是原表达式的 parameter</remarks>
        public static (Expression exp, Dictionary<ParameterExpression, object> midValues) ReduceLambda(LambdaExpression lambdaExpression, Func<Expression, bool> isKeepCallBack = null, List<ParameterExpression> initParameters = null)
        {
            var root = new ExpressionNode
            {
                Expression = lambdaExpression,
                _parameters = new List<ParameterExpression>(),
                _outMidValues = new Dictionary<ParameterExpression, object>(),
                Children = new List<ExpressionNode>(),
                IsRoot = true,
            };
            if (initParameters.IsNotNullOrEmpty()) root._parameters.AddRange(initParameters);
            VisitTree(root);
            //将()=>Param_0 转为: ()=>Constant
            if (root.Children[0].Expression is ParameterExpression parameterExpression && root.outMidValues.TryGetValue(parameterExpression, out var obj))
            {
                root.Children[0] = new ExpressionNode
                {
                    Parent = root,
                    Expression = Expression.Constant(obj, parameterExpression.Type),
                    _updateRequest = true,
                    FullMarkString = parameterExpression.Type.GetClassFullName()
                };
            }

            if (root.Children[0].NodeType == ExpressionType.Constant)
            {
                return (root.Children[0].Expression, root.outMidValues);
            }

            root.FullMarkString = $"({lambdaExpression.Parameters.Select(i => $"{i.Type.GetClassFullName()} {i.Name}").ToStringSeparated(",")})=>{root.Children[0].FullMarkString}";
            return (root.Expression, root.outMidValues);

            //遍历,标记出来唯一值
            void VisitTree(ExpressionNode node)
            {
                if (node.Expression == null) return;
                var visit = GetVisit(node.NodeType.Value);
                visit.Reduce(node, VisitTree, isKeepCallBack);
            }
        }

        /// <summary>
        /// 调试用,获取 reduce 方法中用到的 FullMarkString
        /// </summary>
        public static string GetFullMarkString(LambdaExpression lambdaExpression)
        {
            var root = new ExpressionNode
            {
                Expression = lambdaExpression,
                _parameters = new List<ParameterExpression>(),
                Children = new List<ExpressionNode>()
            };
            VisitTree(root);
            return root.FullMarkString;

            //遍历,标记出来唯一值
            void VisitTree(ExpressionNode node)
            {
                if (node.Expression == null) return;
                var visit = GetVisit(node.NodeType.Value);
                visit.Reduce(node, VisitTree, null, true);
            }
        }
        private static Dictionary<ExpressionType, BaseVisit> _caches = new Dictionary<ExpressionType, BaseVisit>()
        {
           #region 所有的节点类型
            { ExpressionType.Add, new AddVisit() },
            { ExpressionType.AddChecked, new AddCheckedVisit() },
            { ExpressionType.And, new AndVisit() },
            { ExpressionType.AndAlso, new AndAlsoVisit() },
            { ExpressionType.ArrayLength, new ArrayLengthVisit() },
            { ExpressionType.ArrayIndex, new ArrayIndexVisit() },
            { ExpressionType.Call, new CallVisit() },
            { ExpressionType.Coalesce, new CoalesceVisit() },
            { ExpressionType.Conditional, new ConditionalVisit() },
            { ExpressionType.Constant, new ConstantVisit() },
            { ExpressionType.Convert, new ConvertVisit() },
            { ExpressionType.ConvertChecked, new ConvertCheckedVisit() },
            { ExpressionType.Divide, new DivideVisit() },
            { ExpressionType.Equal, new EqualVisit() },
            { ExpressionType.ExclusiveOr, new ExclusiveOrVisit() },
            { ExpressionType.GreaterThan, new GreaterThanVisit() },
            { ExpressionType.GreaterThanOrEqual, new GreaterThanOrEqualVisit() },
            { ExpressionType.Invoke, new InvokeVisit() },
            { ExpressionType.Lambda, new LambdaVisit() },
            { ExpressionType.LeftShift, new LeftShiftVisit() },
            { ExpressionType.LessThan, new LessThanVisit() },
            { ExpressionType.LessThanOrEqual, new LessThanOrEqualVisit() },
            { ExpressionType.ListInit, new ListInitVisit() },
            { ExpressionType.MemberAccess, new MemberAccessVisit() },
            { ExpressionType.MemberInit, new MemberInitVisit() },
            { ExpressionType.Modulo, new ModuloVisit() },
            { ExpressionType.Multiply, new MultiplyVisit() },
            { ExpressionType.MultiplyChecked, new MultiplyCheckedVisit() },
            { ExpressionType.Negate, new NegateVisit() },
            { ExpressionType.UnaryPlus, new UnaryPlusVisit() },
            { ExpressionType.NegateChecked, new NegateCheckedVisit() },
            { ExpressionType.New, new NewVisit() },
            { ExpressionType.NewArrayInit, new NewArrayInitVisit() },
            { ExpressionType.NewArrayBounds, new NewArrayBoundsVisit() },
            { ExpressionType.Not, new NotVisit() },
            { ExpressionType.NotEqual, new NotEqualVisit() },
            { ExpressionType.Or, new OrVisit() },
            { ExpressionType.OrElse, new OrElseVisit() },
            { ExpressionType.Parameter, new ParameterVisit() },
            { ExpressionType.Power, new PowerVisit() },
            { ExpressionType.Quote, new QuoteVisit() },
            { ExpressionType.RightShift, new RightShiftVisit() },
            { ExpressionType.Subtract, new SubtractVisit() },
            { ExpressionType.SubtractChecked, new SubtractCheckedVisit() },
            { ExpressionType.TypeAs, new TypeAsVisit() },
            { ExpressionType.TypeIs, new TypeIsVisit() },
            { ExpressionType.Assign, new AssignVisit() },
            { ExpressionType.Block, new BlockVisit() },
            { ExpressionType.DebugInfo, new DebugInfoVisit() },
            { ExpressionType.Decrement, new DecrementVisit() },
            { ExpressionType.Dynamic, new DynamicVisit() },
            { ExpressionType.Default, new DefaultVisit() },
            { ExpressionType.Extension, new ExtensionVisit() },
            { ExpressionType.Goto, new GotoVisit() },
            { ExpressionType.Increment, new IncrementVisit() },
            { ExpressionType.Index, new IndexVisit() },
            { ExpressionType.Label, new LabelVisit() },
            { ExpressionType.RuntimeVariables, new RuntimeVariablesVisit() },
            { ExpressionType.Loop, new LoopVisit() },
            { ExpressionType.Switch, new SwitchVisit() },
            { ExpressionType.Throw, new ThrowVisit() },
            { ExpressionType.Try, new TryVisit() },
            { ExpressionType.Unbox, new UnboxVisit() },
            { ExpressionType.AddAssign, new AddAssignVisit() },
            { ExpressionType.AndAssign, new AndAssignVisit() },
            { ExpressionType.DivideAssign, new DivideAssignVisit() },
            { ExpressionType.ExclusiveOrAssign, new ExclusiveOrAssignVisit() },
            { ExpressionType.LeftShiftAssign, new LeftShiftAssignVisit() },
            { ExpressionType.ModuloAssign, new ModuloAssignVisit() },
            { ExpressionType.MultiplyAssign, new MultiplyAssignVisit() },
            { ExpressionType.OrAssign, new OrAssignVisit() },
            { ExpressionType.PowerAssign, new PowerAssignVisit() },
            { ExpressionType.RightShiftAssign, new RightShiftAssignVisit() },
            { ExpressionType.SubtractAssign, new SubtractAssignVisit() },
            { ExpressionType.AddAssignChecked, new AddAssignCheckedVisit() },
            { ExpressionType.MultiplyAssignChecked, new MultiplyAssignCheckedVisit() },
            { ExpressionType.SubtractAssignChecked, new SubtractAssignCheckedVisit() },
            { ExpressionType.PreIncrementAssign, new PreIncrementAssignVisit() },
            { ExpressionType.PreDecrementAssign, new PreDecrementAssignVisit() },
            { ExpressionType.PostIncrementAssign, new PostIncrementAssignVisit() },
            { ExpressionType.PostDecrementAssign, new PostDecrementAssignVisit() },
            { ExpressionType.TypeEqual, new TypeEqualVisit() },
            { ExpressionType.OnesComplement, new OnesComplementVisit() },
            { ExpressionType.IsTrue, new IsTrueVisit() },
            { ExpressionType.IsFalse, new IsFalseVisit() }, 
	#endregion
        };

        internal static BaseVisit GetVisit(ExpressionType expressionType)
        {
            var visit = _caches[expressionType];
            return visit;
        }
    }
}
